package cn.edu.usst.cs.goldfish.booking.meetingroom.web.controller;

import cn.edu.usst.cs.goldfish.booking.meetingroom.model.Meeting;
import cn.edu.usst.cs.goldfish.booking.meetingroom.model.MeetingAttendee;
import cn.edu.usst.cs.goldfish.booking.meetingroom.model.MeetingRoom;
import cn.edu.usst.cs.goldfish.booking.meetingroom.model.SysUser;
import cn.edu.usst.cs.goldfish.booking.meetingroom.mybatis.ExceptionMapper;
import cn.edu.usst.cs.goldfish.booking.meetingroom.mybatis.MeetingAttendeeMapper;
import cn.edu.usst.cs.goldfish.booking.meetingroom.mybatis.MeetingMapper;
import cn.edu.usst.cs.goldfish.booking.meetingroom.mybatis.MeetingRoomMapper;
import cn.edu.usst.cs.goldfish.booking.meetingroom.mybatis.UserMapper;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.health.HealthComponent;
import org.springframework.boot.actuate.health.SystemHealth;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.kafka.core.KafkaAdmin;
import org.apache.kafka.clients.admin.AdminClient;
import org.apache.kafka.clients.admin.DescribeTopicsResult;
import org.apache.kafka.clients.admin.ListOffsetsResult;
import org.apache.kafka.clients.admin.TopicDescription;
import org.apache.kafka.common.TopicPartition;
// import org.apache.kafka.clients.admin.ListOffsetsResult.ListOffsetsResultInfo;
// import org.apache.kafka.common.requests.ListOffsetsRequest;
import org.apache.kafka.clients.admin.OffsetSpec;
import cn.edu.usst.cs.goldfish.booking.meetingroom.web.dto.KafkaTopicStatsDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import org.springframework.data.redis.core.StringRedisTemplate;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import java.io.*;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;

@Slf4j
@RestController
@RequestMapping("/admin")
@RequiredArgsConstructor
public class AdminController {

    private final MeetingMapper meetingMapper;
    private final UserMapper userMapper;
    private final MeetingRoomMapper meetingRoomMapper;
    private final MeetingAttendeeMapper meetingAttendeeMapper;
    private final HealthEndpoint healthEndpoint;
    private final KafkaAdmin kafkaAdmin;
    private final StringRedisTemplate stringRedisTemplate;
    private final ExceptionMapper exceptionMapper;

    // 报表导出接口（返回文件下载链接或直接导出Excel）
    @GetMapping(value = "/exportReport", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
    public ResponseEntity<byte[]> exportReport() {
        try {
            List<Meeting> meetings = meetingMapper.selectList(null);
            List<SysUser> users = userMapper.selectList(new QueryWrapper<SysUser>().orderByDesc("id").last("limit 100"));
            List<MeetingRoom> rooms = meetingRoomMapper.selectList(new QueryWrapper<MeetingRoom>().orderByDesc("id").last("limit 100"));
            List<MeetingAttendee> attendees = meetingAttendeeMapper.selectList(new QueryWrapper<MeetingAttendee>().orderByDesc("id").last("limit 100"));

            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            com.alibaba.excel.ExcelWriter excelWriter = EasyExcel.write(outputStream).build();

            // 会议 sheet
            com.alibaba.excel.write.metadata.WriteSheet meetingSheet = EasyExcel.writerSheet("会议").build();
            excelWriter.write(meetings, meetingSheet);

            // 用户 sheet
            com.alibaba.excel.write.metadata.WriteSheet userSheet = EasyExcel.writerSheet("用户").build();
            excelWriter.write(users, userSheet);

            // 会议室 sheet
            com.alibaba.excel.write.metadata.WriteSheet roomSheet = EasyExcel.writerSheet("会议室").build();
            excelWriter.write(rooms, roomSheet);

            // 参会人 sheet
            com.alibaba.excel.write.metadata.WriteSheet attendeeSheet = EasyExcel.writerSheet("参会人").build();
            excelWriter.write(attendees, attendeeSheet);

            excelWriter.finish();

            byte[] excelData = outputStream.toByteArray();

            return ResponseEntity.ok()
                    .header("Content-Disposition", "attachment; filename=meeting_report.xlsx")
                    .header("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
                    .body(excelData);

        } catch (Exception e) {
            exceptionMapper.insert(e);
            byte[] errorData = ("导出失败: " + e.getMessage()).getBytes();
            return ResponseEntity.ok()
                    .header("Content-Disposition", "attachment; filename=error.txt")
                    .header("Content-Type", "text/plain")
                    .body(errorData);
        }
    }

    // Kafka可视化接口（返回Kafka状态/消息统计等）
    @GetMapping("/kafkaStatus")
    public ResponseEntity<Map<String, Object>> kafkaStatus() {
        HealthComponent kafkaStatus = null;
        HealthComponent healthComponent = healthEndpoint.health();
        if (healthComponent instanceof SystemHealth systemHealth) {
            Map<String, HealthComponent> components = systemHealth.getComponents();
            if (components != null && components.containsKey("kafka")) {
                kafkaStatus = components.get("kafka");
            }
        }

        // 消息统计
        long totalMessages = 0;
        ArrayList<KafkaTopicStatsDTO> topicStatsList = new ArrayList<>();
        try (AdminClient adminClient = AdminClient.create(kafkaAdmin.getConfigurationProperties())) {
            Set<String> topicNames = adminClient.listTopics().names().get();
            DescribeTopicsResult describeTopicsResult = adminClient.describeTopics(topicNames);
            Map<String, TopicDescription> topicDescMap = new HashMap<>();
            Map<String, org.apache.kafka.common.KafkaFuture<TopicDescription>> futures = describeTopicsResult.topicNameValues();
            for (Map.Entry<String, org.apache.kafka.common.KafkaFuture<TopicDescription>> entry : futures.entrySet()) {
                topicDescMap.put(entry.getKey(), entry.getValue().get());
            }

            for (String topic : topicNames) {
                TopicDescription desc = topicDescMap.get(topic);
                if (desc == null) continue;
                long topicMessageCount = 0;
                for (var partition : desc.partitions()) {
                    TopicPartition tp = new TopicPartition(topic, partition.partition());
                    ListOffsetsResult listOffsets = adminClient.listOffsets(
                            Map.of(tp, OffsetSpec.latest()));
                    long endOffset = listOffsets.partitionResult(tp).get().offset();
                    ListOffsetsResult listOffsetsEarliest = adminClient.listOffsets(
                            Map.of(tp, OffsetSpec.earliest()));
                    long startOffset = listOffsetsEarliest.partitionResult(tp).get().offset();
                    topicMessageCount += (endOffset - startOffset);
                }
                totalMessages += topicMessageCount;
                KafkaTopicStatsDTO dto = new KafkaTopicStatsDTO();
                dto.setTopic(topic);
                dto.setPartitionCount(desc.partitions().size());
                dto.setMessageCount(topicMessageCount);
                topicStatsList.add(dto);
            }
        } catch (Exception e) {
            // ignore or log
        }

        Map<String, Object> result = new HashMap<>();
        result.put("kafkaStatus", kafkaStatus != null ? kafkaStatus.toString() : "未检测到Kafka健康信息");
        result.put("totalMessages", totalMessages);
        result.put("topics", topicStatsList);

        return ResponseEntity.ok(result);
    }

    // Redis可视化接口（返回Redis缓存统计等）
    @GetMapping("/redisStatus")
    public ResponseEntity<Map<String, Object>> redisStatus() {
        Map<String, Object> result = new HashMap<>();
        try {
            // 获取 Redis info
            Properties info = stringRedisTemplate.getConnectionFactory().getConnection().info("all");
            // 你可以提取常用指标
            result.put("redis_version", info.getProperty("redis_version"));
            result.put("connected_clients", info.getProperty("connected_clients"));
            result.put("used_memory_human", info.getProperty("used_memory_human"));
            result.put("total_commands_processed", info.getProperty("total_commands_processed"));
            result.put("key_count", stringRedisTemplate.keys("*").size());
            result.put("cache_hit_rate", info.getProperty("keyspace_hits") + "/" + info.getProperty("keyspace_misses"));
            // 你可以根据需要添加更多字段
        } catch (Exception e) {
            result.put("error", e.getMessage());
        }
        return ResponseEntity.ok(result);
    }

    // Redis详细信息接口（列出每个key的详细信息）
    @GetMapping("/redisDetails")
    public ResponseEntity<Map<String, Object>> redisDetails() {
        Map<String, Object> result = new HashMap<>();
        try {
            // 获取所有key
            Set<String> keys = stringRedisTemplate.keys("*");
            List<Map<String, Object>> keyDetails = new ArrayList<>();
            
            for (String key : keys) {
                Map<String, Object> keyInfo = new HashMap<>();
                keyInfo.put("key", key);
                
                try {
                    // 获取key的类型
                    String type = stringRedisTemplate.getConnectionFactory().getConnection().type(key.getBytes()).toString();
                    keyInfo.put("type", type);
                    
                    // 获取key的TTL（过期时间）
                    Long ttl = stringRedisTemplate.getExpire(key);
                    keyInfo.put("ttl", ttl != null ? ttl : -1);
                    
                    // 根据类型获取详细信息
                    switch (type) {
                        case "string":
                            String value = stringRedisTemplate.opsForValue().get(key);
                            keyInfo.put("value", value); // 返回完整值
                            keyInfo.put("size", value != null ? value.length() : 0);
                            keyInfo.put("preview", value != null ? value.substring(0, Math.min(value.length(), 100)) + (value.length() > 100 ? "..." : "") : null);
                            break;
                        case "hash":
                            Map<Object, Object> hashValue = stringRedisTemplate.opsForHash().entries(key);
                            keyInfo.put("value", hashValue); // 返回完整hash
                            keyInfo.put("size", hashValue.size());
                            keyInfo.put("preview", hashValue.entrySet().stream()
                                    .limit(3)
                                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
                            break;
                        case "list":
                            Long listSize = stringRedisTemplate.opsForList().size(key);
                            List<String> listValue = stringRedisTemplate.opsForList().range(key, 0, -1); // 获取所有元素
                            keyInfo.put("value", listValue); // 返回完整list
                            keyInfo.put("size", listSize);
                            keyInfo.put("preview", listValue != null ? listValue.stream().limit(3).collect(Collectors.toList()) : null);
                            break;
                        case "set":
                            Long setSize = stringRedisTemplate.opsForSet().size(key);
                            Set<String> setValue = stringRedisTemplate.opsForSet().members(key);
                            keyInfo.put("value", setValue); // 返回完整set
                            keyInfo.put("size", setSize);
                            keyInfo.put("preview", setValue != null ? setValue.stream().limit(3).collect(Collectors.toList()) : null);
                            break;
                        case "zset":
                            Long zsetSize = stringRedisTemplate.opsForZSet().size(key);
                            Set<String> zsetValue = stringRedisTemplate.opsForZSet().range(key, 0, -1); // 获取所有元素
                            keyInfo.put("value", zsetValue); // 返回完整zset
                            keyInfo.put("size", zsetSize);
                            keyInfo.put("preview", zsetValue != null ? zsetValue.stream().limit(3).collect(Collectors.toList()) : null);
                            break;
                        default:
                            keyInfo.put("value", "不支持的类型");
                            keyInfo.put("preview", "不支持的类型");
                            break;
                    }
                    
                    // 获取key的创建时间（通过TTL估算）
                    if (ttl != null && ttl > 0) {
                        keyInfo.put("estimated_creation", System.currentTimeMillis() - (ttl * 1000));
                    }
                    
                } catch (Exception e) {
                    keyInfo.put("error", e.getMessage());
                }
                
                keyDetails.add(keyInfo);
            }
            
            result.put("total_keys", keys.size());
            result.put("keys", keyDetails);
            
            // 按类型统计
            Map<String, Long> typeStats = keyDetails.stream()
                    .map(k -> (String) k.get("type"))
                    .collect(Collectors.groupingBy(
                            type -> type != null ? type : "unknown",
                            Collectors.counting()
                    ));
            result.put("type_statistics", typeStats);
            
        } catch (Exception e) {
            result.put("error", e.getMessage());
            log.error("获取Redis详细信息失败", e);
        }
        return ResponseEntity.ok(result);
    }

    // 统计接口
    @GetMapping("/stats")
    public Map<String, Object> getStats() {
        Map<String, Object> map = new HashMap<>();
        map.put("meetingCount", meetingMapper.selectCount(null));
        map.put("userCount", userMapper.selectCount(null));
        map.put("roomCount", meetingRoomMapper.selectCount(null));
        map.put("attendeeCount", meetingAttendeeMapper.selectCount(null));
        return map;
    }
} 